﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using CommonFramework.Log;
using CommonFramework.Serialize.BytesArray;
using CommonFramework.Time;
using Net.Proto;
using Net.Server;
using Net;
using Net.Channel;

namespace Net.Client
{
    public class NetClientManager:NetManagerBase
    {
        //Tcp会话
        private NetSession _tcpTcpSession = null;
        private NetTcpChannel _tcpChannel=>_tcpTcpSession?.Channel as NetTcpChannel;
        public NetSession TcpSession => _tcpTcpSession;

        //Udp会话
        private NetSession _udpSession = null;
        private NetUdpChannel _udpChannel=>_udpSession?.Channel as NetUdpChannel;
        public NetSession UdpSession => _udpSession;

        #region 消息包

        /// <summary>
        /// 已经解析出等待处理的消息包.
        /// 消息包是在多线程环境下被解析出来添加到列表中的,可在主线程或指定的线程处理消息包列表
        /// </summary>
        private  List<NetPacket> _packetList = new List<NetPacket>();
        /// <summary>
        /// 消息包列表的数量
        /// </summary>
        private  int _packetCount = 0;
        /// <summary>
        /// 每次Update时最多处理的消息包数
        /// </summary>
        private const int MAX_MSG_FIRE = 10;
        /// <summary>
        /// 在执行Update的线程中处理解析出的消息包
        /// </summary>
        private void ConsumePakcet()
        {
            if (_packetCount <= 0)
                return;
            for (int i = 0; i < MAX_MSG_FIRE; ++i)
            {
                NetPacket packet = null;
                lock (_packetList)
                {
                    if (_packetList.Count > 0)
                    {
                        packet = _packetList[0];
                        _packetList.RemoveAt(0);
                        _packetCount--;
                    }
                }

                if (packet != null)
                {
                    FirePacket(packet);
                }
                else
                {
                    break;
                }
            }
        }
        public override void OnReceivePacket(NetSession session, NetPacket packet)
        {
            lock (_packetList)
            {
                _packetList.Add(packet);
                _packetCount++;
            }
        }

        #endregion

        private void OnInit()
        {
            //重置消息包列表
            _packetList = new List<NetPacket>();
            _packetCount = 0;

            //心跳
            string pong= NetUtil.GetPacketName<NetPacketPong>();
            if (!ContainsPacketHandler(pong))//考虑可能多次断线重连,Init会被调用多次.Pong消息回调只需要注册一次
            {
                AddPacketHandler(pong,OnPongPacket);
            }
        }

        public void OnUpdate()
        {
            ProcessPingUpdate();
            ConsumePakcet();
        }


        #region 消息包事件监听回调

        public delegate void PacketArriveHandler(NetPacket msg);
        private  Dictionary<string, PacketArriveHandler> _packetHandlers =
            new Dictionary<string, PacketArriveHandler>();

        public void AddPacketHandler(string name, PacketArriveHandler handler)
        {
            if (string.IsNullOrEmpty(name) || handler == null)
                return;

            if (_packetHandlers.ContainsKey(name))
                _packetHandlers[name] += handler;
            else
                _packetHandlers[name] = handler;
        }
        public void RemovePacketHandler(string name, PacketArriveHandler handler)
        {
            if (string.IsNullOrEmpty(name) || handler == null)
                return;
            if (_packetHandlers.ContainsKey(name))
            {
                _packetHandlers[name] -= handler;
                if (_packetHandlers[name] == null)
                    _packetHandlers.Remove(name);
            }
        }
        public bool ContainsPacketHandler(string name)
        {
            if (string.IsNullOrEmpty(name))
                return false;
            if (_packetHandlers.ContainsKey(name) == false)
                return false;
            return _packetHandlers[name] != null;
        }
        public void FirePacket(NetPacket packet)
        {
            string name = packet?.PacketName;
            if (string.IsNullOrEmpty(name))
                return;
            if(_packetHandlers.ContainsKey(name))
                _packetHandlers[name].Invoke(packet);
        }

        #endregion


        /// <summary>
        /// 发起TCP连接
        /// </summary>
        public void TcpConnect(string ip, int port)
        {
            Close(TcpSession?.Socket);

            OnInit();

            //开始连接
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            {
                NoDelay = true
            };
            _tcpTcpSession = new NetSession(clientSocket,this, (session)=>new NetTcpChannel(session));
            _tcpChannel.Connect(ip,port);
        }
        /// <summary>
        /// 发送消息包
        /// </summary>
        public void TcpSend(NetPacket packet)
        {
            _tcpTcpSession?.Send(packet);
        }

        /// <summary>
        /// 设置Udp要接受的消息端口
        /// </summary>
        public void UdpBind(int localPort,IPEndPoint sendTo)
        {
            Close(_udpSession?.Socket);

            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            clientSocket.Bind(new IPEndPoint(IPAddress.Any, localPort));

            _udpSession = new NetSession(clientSocket, this, (session)=>new NetUdpChannel(session, sendTo));
            _udpChannel.BeginReceive();
        }
        public void UdpSend(NetPacket packet)
        {
            _udpSession?.Send(packet);
        }

        /// <summary>
        /// 关闭已经建立的连接
        /// </summary>
        public override void Close(Socket socket)
        {
            if (_tcpTcpSession != null && _tcpTcpSession.Socket==socket)
            {
                NetEventHandler.OnDisconnect(_tcpTcpSession);
                _tcpTcpSession.Close();
            }

            if (_udpSession != null && _udpSession.Socket == socket)
            {
                NetEventHandler.OnDisconnect(_udpSession);
                _udpSession.Close();
            }
        }

        #region 心跳机制

        private void ProcessPingUpdate()
        {
            if (!IsUsingPing || _tcpTcpSession==null || _tcpTcpSession.IsClosed())
                return;

            long now= TimeUtil.NowTimestampSeconds();

            //发送ping
            if (now - _tcpTcpSession.LastHeartPingTime > PingInterval)
            {
                NetPacketPing ping = new NetPacketPing();
                this.TcpSend(ping);
                _tcpTcpSession.LastHeartPingTime = now;
            }

            //接收pong超时则关闭连接
            if (_tcpTcpSession.LastHreatPongTime>0 && (now - _tcpTcpSession.LastHreatPongTime > 4 * PingInterval))
            {
                this.Close(TcpSession?.Socket);
            }
        }
        private  void OnPongPacket(NetPacket msg)
        {
            if(_tcpTcpSession!=null)
                _tcpTcpSession.LastHreatPongTime = TimeUtil.NowTimestampSeconds();
        }
        #endregion
    }
}